﻿namespace OA.Infrastructure.Impl;

internal class DepartmentService : IDepartmentService
{
    private readonly DbContext _context;

    public DepartmentService(DbContext context)
    {
        _context = context;
    }

    public async Task<ListResult<Department.QueryModel>> GetListAsync(CancellationToken cancellationToken)
    {
        var result = new ListResult<Department.QueryModel>();

        try
        {
            var departments = await _context.Set<Department>()
                .Include(x => x.Leader)
                .Include(x => x.Parent)
                .Include(x => x.Roles)
                .AsNoTracking()
                .ToListAsync(cancellationToken);

            result.Data = departments.Select(x => GetQueryModel(x));

            result.Status = OperationalResult.SUCCESS;
        }
        catch (Exception error)
        {
            result.Status = OperationalResult.ERROR;

            result.Message = error.ToString();
        }

        return result;
    }

    public async Task<PageResult<Department.QueryModel>> GetPageAsync(Department.ConditionSelectorCondition condition, CancellationToken cancellationToken)
    {
        var result = new PageResult<Department.QueryModel>();

        try
        {
            var expression = GetConditionSelectorCondition(condition);

            var data = await _context.Set<Department>()
                .Include(x => x.Leader).ThenInclude(x => x.Department)
                .Include(x => x.Leader).ThenInclude(x => x.DirectLeader)
                .Include(x => x.Leader).ThenInclude(x => x.Roles)
                .Include(x => x.Parent).ThenInclude(x => x.Leader).ThenInclude(x => x.DirectLeader)
                .Include(x => x.Parent).ThenInclude(x => x.Leader).ThenInclude(x => x.Department)
                .Include(m => m.Roles)
                .Where(expression)
                .OrderBy(x => x.Id)
                .Skip((condition.PageNum - 1) * condition.PageSize)
                .Take(condition.PageSize)
                .AsNoTracking()
                .ToListAsync(cancellationToken);

            result.Data = data.Select(x => GetQueryModel(x));

            result.PageNum = condition.PageNum;

            result.PageSize = condition.PageSize;

            result.Total = await _context.Set<Department>().CountAsync(expression, cancellationToken);

            result.Status = OperationalResult.SUCCESS;
        }
        catch (Exception error)
        {
            result.Status = OperationalResult.ERROR;

            result.Message = error.ToString();
        }

        return result;
    }

    public async Task<OperationalResult> CreateAsync(Department.CreateModel department, CancellationToken cancellationToken)
    {
        var result = new OperationalResult();

        try
        {
            var entity = new Department
            {
                Name = department.Name,
            };

            if (department.LeaderId is not null)
            {
                var entityLeader = await _context.Set<User>().FindAsync(new object[] { department.LeaderId }, cancellationToken);

                if (entityLeader is not null)
                {
                    entity.Leader = entityLeader;
                }
                else
                {
                    result.Status = OperationalResult.ERROR;
                    result.Message = $"不存在Id为{department.LeaderId}的人员";

                    return result;
                }
            }

            if (department.ParentId is not null)
            {
                var entityParent = await _context.Set<Department>().FindAsync(new object[] { department.ParentId }, cancellationToken);

                if (entityParent is not null)
                {
                    entity.Parent = entityParent;
                }
                else
                {
                    result.Status = OperationalResult.ERROR;
                    result.Message = $"不存在Id为{department.ParentId}的部门";

                    return result;
                }
            }

            foreach (var roleId in department.RolesId)
            {
                var role = await _context.Set<Role>().FindAsync(new object[] { roleId }, cancellationToken);

                if (role is not null)
                {
                    entity.Roles.Add(role);
                }
            }

            await _context.Set<Department>().AddAsync(entity, cancellationToken);

            var affectedRows = await _context.SaveChangesAsync(cancellationToken);

            result.Status = affectedRows > 0 ? OperationalResult.SUCCESS : OperationalResult.FAILURE;

            result.Message = $"{affectedRows} rows affected.";
        }
        catch (Exception error)
        {
            result.Status = OperationalResult.ERROR;

            result.Message = error.ToString();
        }

        return result;
    }

    public async Task<OperationalResult> UpdateAsync(int id, Department.UpdateModel department, CancellationToken cancellationToken)
    {
        var result = new OperationalResult();

        try
        {
            var entity = await _context.Set<Department>()
                .Include(x => x.Leader)
                .Include(n => n.Roles)
                .Where(m => m.Id.Equals(id))
                .FirstOrDefaultAsync(cancellationToken);

            if (entity is null)
            {
                result.Status = OperationalResult.FAILURE;

                result.Message = "0 rows affected.";

                return result;
            }

            entity.Name = department.Name;

            entity.Roles.Clear();

            if (department.LeaderId is not null)
            {
                var entityLeader = await _context.Set<User>().FindAsync(new object[] { department.LeaderId }, cancellationToken);

                if (entityLeader is not null)
                {
                    entity.Leader = entityLeader;
                }
                else
                {
                    result.Status = OperationalResult.ERROR;
                    result.Message = $"不存在Id为{department.LeaderId}的人员";

                    return result;
                }
            }

            if (department.ParentId is not null)
            {
                var entityParent = await _context.Set<Department>().FindAsync(new object[] { department.ParentId }, cancellationToken);

                if (entityParent is not null)
                {
                    entity.Parent = entityParent;
                }
                else
                {
                    result.Status = OperationalResult.ERROR;
                    result.Message = $"不存在Id为{department.ParentId}的部门";

                    return result;
                }
            }

            foreach (var roleId in department.RolesId)
            {
                var role = await _context.Set<Role>().FindAsync(new object[] { roleId }, cancellationToken);

                if (role is not null)
                {
                    entity.Roles.Add(role);
                }
            }

            _context.Set<Department>().Update(entity);

            var affectedRows = await _context.SaveChangesAsync(cancellationToken);

            result.Status = affectedRows > 0 ? OperationalResult.SUCCESS : OperationalResult.FAILURE;

            result.Message = $"{affectedRows} rows affected.";
        }
        catch (Exception error)
        {
            var exists = await _context.Set<Department>().AnyAsync(m => m.Id.Equals(id), cancellationToken);

            if (!exists)
            {
                result.Status = OperationalResult.FAILURE;

                result.Message = "0 rows affected.";
            }
            else
            {
                result.Status = OperationalResult.ERROR;

                result.Message = error.ToString();
            }
        }

        return result;
    }

    public async Task<OperationalResult> DeleteAsync(int id, CancellationToken cancellationToken)
    {
        var result = new OperationalResult();

        try
        {
            var department = await _context.Set<Department>()
                .Include(x => x.Leader)
                .Include(x => x.Roles)
                .Where(m => m.Id.Equals(id))
                .FirstOrDefaultAsync(cancellationToken);

            if (department is null)
            {
                result.Status = OperationalResult.FAILURE;

                result.Message = "0 rows affected.";

                return result;
            }

            _context.Set<Department>().Remove(department);

            var affectedRows = await _context.SaveChangesAsync(cancellationToken);

            result.Status = affectedRows > 0 ? OperationalResult.SUCCESS : OperationalResult.FAILURE;

            result.Message = $"{affectedRows} rows affected.";
        }
        catch (Exception error)
        {
            result.Status = OperationalResult.ERROR;

            result.Message = error.ToString();
        }

        return result;
    }

    private static Expression<Func<Department, bool>> GetConditionSelectorCondition(Department.ConditionSelectorCondition condition)
    {
        Expression<Func<Department, bool>>? expression = null;

        if (condition.SelectedRoleId is not null)
        {
            Expression<Func<Department, bool>>? selectedRole = null;

            selectedRole = x => x.Roles.Any(x => condition.SelectedRoleId == x.Id);
            expression = expression?.And(selectedRole) ?? selectedRole;
        }

        return expression ?? (x => true);
    }

    internal static Department.QueryModel GetQueryModel(Department department)
    {
        return new Department.QueryModel
        {
            Id = department.Id,
            CreatedDate = department.CreatedDate,
            LastModifiedDate = department.LastModifiedDate,
            Name = department.Name,
            Leader = department.Leader is not null ? UserService.GetQueryModel(department.Leader) : null,
            Parent = department.Parent is not null ? GetQueryModel(department.Parent) : null,
            Roles = department.Roles.Select(x => RoleService.GetQueryModel(x)),
        };
    }

    internal static Department.QueryModel GetLimitedQueryModel(Department department)
    {
        return new Department.QueryModel
        {
            Id = department.Id,
            CreatedDate = department.CreatedDate,
            LastModifiedDate = department.LastModifiedDate,
            Name = department.Name,
            Leader = department.Leader is not null ? new User.QueryModel
            {
                Id = department.Leader.Id,
                Name = department.Leader.Name
            } : null,
            Parent = department.Parent is not null ? GetQueryModel(department.Parent) : null,
            Roles = department.Roles.Select(x => RoleService.GetQueryModel(x)),
        };
    }
}